//
// Created by 15229 on 2022/11/19.
//

#include "SyncUtils.h"

///////////////////////////////////////// ConditionVariable /////////////////////////////////////////////////////////////
ConditionVariable::ConditionVariable(bool state)
        : mCondition(state) {
}

void ConditionVariable::close() noexcept {
    std::lock_guard<std::mutex> guard(mMutex);
    mCondition = false;
}

void ConditionVariable::open() noexcept {
    std::lock_guard<std::mutex> guard(mMutex);
    bool old = mCondition;
    mCondition = true;
    if (!old) {
        mConditionVariable.notify_all();
    }
}

void ConditionVariable::block(int64_t timeout) noexcept {
    std::unique_lock<std::mutex> lock(mMutex);
    if (mCondition) {
        return;
    }

    if(timeout < 0) {
        mConditionVariable.wait(lock);
    } else {
        mConditionVariable.wait_for(lock, std::chrono::milliseconds(timeout));
    }
}

///////////////////////////////////////// CountDownLatch /////////////////////////////////////////////////////////////
CountDownLatch::CountDownLatch(uint32_t count) : mCount(count) {}

void CountDownLatch::countDown() noexcept {
    std::lock_guard<std::mutex> guard(mMutex);
    if (0 == mCount) {
        return;
    }
    --mCount;
    if (0 == mCount) {
        mConditionVariable.notify_all();
    }
}

void CountDownLatch::await() noexcept {
    std::unique_lock<std::mutex> lock(mMutex);
    mConditionVariable.wait(lock, [this] { return 0 == mCount; });
}

///////////////////////////////////////// BlockBarrier /////////////////////////////////////////////////////////////
BlockBarrier::BlockBarrier(uint32_t count, const char *name) {
    if (name != nullptr) {
        mName = name;
    } else {
        char ptrName[1024];
        sprintf(ptrName, "%p", this);
        mName = std::string("Barrier#").append(ptrName).c_str();
    }

    mTargetNum = count;
    mCount = 0;
}

void BlockBarrier::setEnable(bool enable) {
    mEnabled = enable;
}

void BlockBarrier::await(const char *what) {
    std::unique_lock<std::mutex> lock(mMutex);

    if (!mEnabled) {
        return;
    }

    mCount++;

    printf("[%s] %s\n", what ? what : "-", (mCount >= mTargetNum) ? "Notify\n" : "Wait");

    if (mCount >= mTargetNum) {
        mCount = 0;
        mCv.notify_all();
    } else {
        mCv.wait(lock);
    }
}

std::string BlockBarrier::toString(const char *what) {
    std::lock_guard<std::mutex> guard(mMutex);
    return std::string(what ? what : "")
            .append(" [")
            .append(mName)
            .append(", mEnalbled= ")
            .append(mEnabled ? "true" : "false")
            .append(", totalCount= ")
            .append(std::to_string(mTargetNum))
            .append(", hasCount= ")
            .append(std::to_string(mCount))
            .append("]");
}


///////////////////////////////////////// StateBarrier /////////////////////////////////////////////////////////////

StateBarrier::StateBarrier(uint32_t count, const char *name)
        : mCount(count) {
    if (name != nullptr) {
        mName = name;
    } else {
        char ptrName[1024];
        sprintf(ptrName, "%p", this);
        mName = std::string("Barrier#").append(ptrName).c_str();
    }
}

void StateBarrier::init() {
    std::lock_guard<std::mutex> guard(mMutex);
    std::thread::id tid = std::this_thread::get_id();

    printf("init: %d\n\n", tid);

    auto it = mTidStateMap.find(tid);
    if (it == mTidStateMap.end()) {
        mTidStateMap.insert(std::pair<std::thread::id, int>(tid, 0));
    }
}

void StateBarrier::mark() {
    std::lock_guard<std::mutex> guard(mMutex);
    std::thread::id tid = std::this_thread::get_id();

    auto it = mTidStateMap.find(tid);
    if (it != mTidStateMap.end() && (it->second != 1)) {
        printf("##### mark: %d\n\n", tid);
        mTidStateMap[tid] = 1;
    }
}

bool StateBarrier::check() {
    std::lock_guard<std::mutex> guard(mMutex);

    if (mTidStateMap.size() != mCount) {
        // Not all thread all initialized
        return false;
    }

    // Check if all state of threads is reach the state
    for (auto&[id, state] : mTidStateMap) {
        if (state == 0) {
            printf("check, thread not mark: %d\n\n", id);
            return false;
        }
    }

    return true;
}

std::string StateBarrier::toString(const char *what) {
    std::lock_guard<std::mutex> guard(mMutex);
    return std::string(what ? what : "")
            .append(" [")
            .append(mName)
            .append(", totalCount= ")
            .append(std::to_string(mCount))
            .append("]");
}
